package cn.always.xiajia.admin.oauth2.service.impl;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import cn.always.xiajia.admin.client.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
import cn.always.xiajia.admin.client.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
import cn.always.xiajia.admin.client.api.oauth2.dto.OAuth2AccessTokenRespDTO;
import cn.always.xiajia.admin.oauth2.entity.OAuth2AccessToken;
import cn.always.xiajia.admin.oauth2.entity.OAuth2Client;
import cn.always.xiajia.admin.oauth2.entity.OAuth2RefreshToken;
import cn.always.xiajia.admin.oauth2.mapper.OAuth2AccessTokenMapper;
import cn.always.xiajia.admin.oauth2.service.OAuth2AccessTokenService;
import cn.always.xiajia.admin.oauth2.service.OAuth2ClientService;
import cn.always.xiajia.admin.oauth2.service.OAuth2RefreshTokenService;
import cn.always.xiajia.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.always.xiajia.framework.common.exception.util.ServiceExceptionUtil;
import cn.always.xiajia.framework.common.util.ExtBeanCopyUtils;
import cn.always.xiajia.framework.common.util.ExtCollUtil;
import cn.always.xiajia.framework.common.util.ExtCollectionUtils;
import cn.always.xiajia.framework.common.util.ExtDateUtils;
import cn.always.xiajia.framework.common.util.ExtObjectUtil;
import cn.always.xiajia.framework.mybatis.core.service.impl.BaseServiceImplX;
import cn.hutool.core.util.IdUtil;

/**
 * 
 * @author xgj
 *
 */
@Service("oAuth2AccessTokenService")
public class OAuth2AccessTokenServiceImpl extends BaseServiceImplX<OAuth2AccessTokenMapper, OAuth2AccessToken>
		implements OAuth2AccessTokenService {

	@Resource
	private OAuth2ClientService oAuth2ClientService;

	@Resource
	private OAuth2RefreshTokenService oAuth2RefreshTokenService;

	/**
	 * 创建访问令牌
	 *
	 * @param reqDTO 访问令牌的创建信息
	 * @return 访问令牌的信息
	 */
	@Override
	public OAuth2AccessTokenRespDTO createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) {
		OAuth2Client oAuth2Client = oAuth2ClientService.validOAuthClientFromCache(reqDTO.getClientId());
		// 创建刷新令牌
		OAuth2RefreshToken oAuth2RefreshToken = createOAuth2RefreshToken(reqDTO, oAuth2Client);
		// 创建访问令牌
		OAuth2AccessToken oAuth2AccessToken = createOAuth2AccessToken(oAuth2RefreshToken, oAuth2Client);
		return ExtBeanCopyUtils.copyProperties(oAuth2AccessToken, OAuth2AccessTokenRespDTO.class);
	}

	/**
	 * 创建刷新令牌
	 * 
	 * @param reqDTO
	 * @return
	 */
	private OAuth2RefreshToken createOAuth2RefreshToken(OAuth2AccessTokenCreateReqDTO reqDTO, OAuth2Client oAuth2Client) {
		OAuth2RefreshToken refreshToken = new OAuth2RefreshToken().setRefreshToken(generateRefreshToken()).setUserId(reqDTO.getUserId())
				.setUserCode(reqDTO.getUserCode()).setUserName(reqDTO.getUserName()).setUserType(reqDTO.getUserType())
				.setClientId(oAuth2Client.getClientId()).setScopes(reqDTO.getScopes())
				.setExpiresTime(LocalDateTime.now().plusSeconds(oAuth2Client.getRefreshTokenValiditySeconds()));
		oAuth2RefreshTokenService.save(refreshToken);
		return refreshToken;
	}

	/**
	 * 创建访问令牌
	 * 
	 * @param reqDTO
	 * @return
	 */
	private OAuth2AccessToken createOAuth2AccessToken(OAuth2RefreshToken oAuth2RefreshToken, OAuth2Client oAuth2Client) {
		OAuth2AccessToken accessTokenDO = new OAuth2AccessToken().setAccessToken(generateAccessToken())
				.setRefreshToken(oAuth2RefreshToken.getRefreshToken()).setUserId(oAuth2RefreshToken.getUserId())
				.setUserCode(oAuth2RefreshToken.getUserCode()).setUserName(oAuth2RefreshToken.getUserName())
				.setUserType(oAuth2RefreshToken.getUserType()).setClientId(oAuth2Client.getClientId())
				.setScopes(oAuth2RefreshToken.getScopes())
				.setExpiresTime(LocalDateTime.now().plusSeconds(oAuth2Client.getAccessTokenValiditySeconds()));
		save(accessTokenDO);
		// 记录到 Redis 中后续开发暂时不用
		return accessTokenDO;
	}

	/**
	 * 生成访问令牌
	 * 
	 * @return
	 */
	private String generateAccessToken() {
		return IdUtil.fastSimpleUUID();
	}

	/**
	 * 生成刷新令牌
	 * 
	 * @return
	 */
	private String generateRefreshToken() {
		return IdUtil.fastSimpleUUID();
	}

	/**
	 * 校验访问令牌
	 *
	 * @param accessToken 访问令牌
	 * @return 访问令牌的信息
	 */
	@Override
	public OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken) {
		Map<String, Object> param = new HashMap<>();
		param.put("EQ_accessToken", accessToken);
		OAuth2AccessToken oAuth2AccessToken = xjGetOne(param);
		if (oAuth2AccessToken == null) {
			throw ServiceExceptionUtil.exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌不存在");
		}
		if (ExtDateUtils.isExpired(oAuth2AccessToken.getExpiresTime())) {
			throw ServiceExceptionUtil.exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌已过期");
		}
		return ExtBeanCopyUtils.copyProperties(oAuth2AccessToken, OAuth2AccessTokenCheckRespDTO.class);
	}

	/**
	 * 移除访问令牌
	 *
	 * @param accessToken 访问令牌
	 * @return 访问令牌的信息
	 */
	@Override
	public OAuth2AccessTokenRespDTO removeAccessToken(String accessToken) {
		// 删除访问令牌
		Map<String, Object> param = new HashMap<>();
		param.put("EQ_accessToken", accessToken);
		OAuth2AccessToken oAuth2AccessToken = xjGetOne(param);
		if (oAuth2AccessToken == null) {
			return null;
		}
		removeById(oAuth2AccessToken.getId());
		// 缓存暂时不用
		// 删除刷新令牌
		param.clear();
		param.put("EQ_refreshToken", oAuth2AccessToken.getRefreshToken());
		oAuth2RefreshTokenService.xjRemove(param);
		return ExtBeanCopyUtils.copyProperties(oAuth2AccessToken, OAuth2AccessTokenRespDTO.class);
	}

	/**
	 * 刷新访问令牌
	 *
	 * @param refreshToken 刷新令牌
	 * @param clientId 客户端编号
	 * @return 访问令牌的信息
	 */
	@Override
	public OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, String clientId) {
		// 查询刷新令牌
		Map<String, Object> param = new HashMap<>();
		param.put("EQ_refreshToken", refreshToken);
		OAuth2RefreshToken oAuth2RefreshToken = oAuth2RefreshTokenService.xjGetOne(param);
		if (oAuth2RefreshToken == null) {
			throw ServiceExceptionUtil.exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "无效的刷新令牌");
		}

		// 校验 Client 匹配
		OAuth2Client oAuth2Client = oAuth2ClientService.validOAuthClientFromCache(clientId);
		if (ExtObjectUtil.notEqual(clientId, oAuth2Client.getClientId())) {
			throw ServiceExceptionUtil.exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "刷新令牌的客户端编号不正确");
		}

		// 移除相关的访问令牌
		List<OAuth2AccessToken> oAuth2AccessTokens = xjList(param);
		if (ExtCollUtil.isNotEmpty(oAuth2AccessTokens)) {
			removeBatchByIds(ExtCollectionUtils.convertSet(oAuth2AccessTokens, OAuth2AccessToken::getId));
			// 缓存删除暂时不用
		}

		// 已过期的情况下，删除刷新令牌
		if (ExtDateUtils.isExpired(oAuth2RefreshToken.getExpiresTime())) {
			oAuth2RefreshTokenService.removeById(oAuth2RefreshToken.getId());
			throw ServiceExceptionUtil.exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "刷新令牌已过期");
		}

		// 创建访问令牌
		OAuth2AccessToken oAuth2AccessToken = createOAuth2AccessToken(oAuth2RefreshToken, oAuth2Client);
		return ExtBeanCopyUtils.copyProperties(oAuth2AccessToken, OAuth2AccessTokenRespDTO.class);
	}

}
